﻿using System;
using System.Collections.Generic;

using Microsoft.Xna.Framework.Content;

namespace Ah.Testudin.Engine.Content
{
    public class TestudinContentManager : ContentManager, ITestudinContentManager
    {
        #region Private Member Variables

        // Keep a list of disposable assets and loaded assets
        private List<IDisposable> disposable = new List<IDisposable>();

        private Dictionary<string, object> loaded = new Dictionary<string, object>();

        #endregion

        #region Properties

        // Whether or not we should keep objects that have been loaded. This
        // way we can avoid loading assets multiple times. However, this may
        // lead to problems with multiple objects changing loaded data, such
        // as effects on a model
        public bool PreserveAssets { get; set; }

        #endregion

        #region Constructors

        // Do nothing in the constructor except inherit from ContentManager
        public TestudinContentManager(IServiceProvider serviceProvider)
            : base(serviceProvider) 
        {
            this.PreserveAssets = true;
        }

        #endregion

        #region Public Methods

        // Override loading of assets so we can use our own functionality
        public override T Load<T>(string assetName)
        {
            if (loaded.ContainsKey(assetName))
            {
                return (T)loaded[assetName];
            }

            // Create a new instance of the requested asset
            T r = this.ReadAsset<T>(assetName, RecordIDisposable);

            // If we are holding on to loaded assets, add it to the list of
            // loaded assets
            if (PreserveAssets && !loaded.ContainsKey(assetName))
                loaded.Add(assetName, r);

            // Return the loaded asset
            return r;
        }

        // Unload all content
        public override void Unload()
        {
            // Dispose all disposable assets
            foreach (IDisposable disp in disposable)
            {
                disp.Dispose();
            }

            // Clear all loaded assets
            loaded.Clear();

            disposable.Clear();
        }

        // Unload a specific piece of content
        public void Unload(string assetName)
        {
            // If the asset has been loaded
            if (loaded.ContainsKey(assetName))
            {
                // If it is disposable, dispose it and take it off the
                // list of disposable content
                if ((loaded[assetName] is IDisposable) &&
                    (disposable.Contains((IDisposable)loaded[assetName])))
                {
                    IDisposable obj = disposable[disposable.IndexOf((IDisposable)loaded[assetName])];

                    obj.Dispose();

                    disposable.Remove(obj);
                }

                // Take it off the list of loaded content
                loaded.Remove(assetName);
            }
        }

        #endregion

        #region Private Methods

        // Internal method to record disposable assets
        private void RecordIDisposable(IDisposable asset)
        {
            // If we are monitoring loaded assets, add it to the list of
            // disposable assets
            if (PreserveAssets)
            {
                disposable.Add(asset);
            }
        }

        #endregion
    }
}
